﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Legend.Collections;
using FakeItEasy; using FakeItEasy.ExtensionSyntax.Full;
using NUnit.Framework;
using System.Collections;
using Legend.Threading;

namespace Legend.Core.Tests.Collections.LockingEnumeratorTests
{
    public class LockingEnumeratorTestCase
    {
        protected LockingEnumerator<string> enumerator;
        protected IEnumerator<string> wrappedEnumerator;
        protected ISynchronizationManager readerWriterLock;

        [SetUp]
        public void SetUp()
        {
            this.OnSetUp();
        }

        protected virtual void OnSetUp()
        {
            readerWriterLock = A.Fake<ISynchronizationManager>();
            wrappedEnumerator = A.Fake<IEnumerator<string>>();
            enumerator = new LockingEnumerator<string>(wrappedEnumerator, readerWriterLock);
        }
    }

    [TestFixture]
    public class Constructor
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_enumerator_is_null()
        {
            var e = new LockingEnumerator<string>(null, A.Fake<ISynchronizationManager>());
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_readerWriterLock_is_null()
        {
            var e = new LockingEnumerator<string>(A.Fake<IEnumerator<string>>(), null);
        }

        [Test]
        public void should_acquire_reader_lock()
        {
            var readerWriterLock = A.Fake<ISynchronizationManager>();

            var enumerator = new LockingEnumerator<string>(A.Fake<IEnumerator<string>>(), readerWriterLock);

            A.CallTo(() => readerWriterLock.EnterReadLock()).MustHaveHappened();
        }
    }

    [TestFixture]
    public class Current
        : LockingEnumeratorTestCase 
    {
        [Test]
        public void should_return_value_from_wrapped_enumerator()
        {
            A.CallTo(() => wrappedEnumerator.Current).Returns("foo");

            string returned = enumerator.Current;
            Assert.AreEqual("foo", returned);
        }

        [Test]
        public void should_return_value_from_wrapped_enumerator_when_called_through_non_generic_interface()
        {
            A.CallTo(() => wrappedEnumerator.Current).Returns("foo");

            object returned = ((IEnumerator)enumerator).Current;
            Assert.AreEqual("foo", returned);
        }
    }

    [TestFixture]
    public class MoveNext
        : LockingEnumeratorTestCase 
    {
        [Test]
        public void should_return_true_when_wrapped_enumerator_returns_true()
        {
            A.CallTo(() => wrappedEnumerator.MoveNext()).Returns(true);

            Assert.IsTrue(enumerator.MoveNext());
        }

        [Test]
        public void should_return_false_when_wrapped_enumerator_returns_false()
        {
            A.CallTo(() => wrappedEnumerator.MoveNext()).Returns(false);

            Assert.IsFalse(enumerator.MoveNext());
        }
    }

    [TestFixture]
    public class Reset
        : LockingEnumeratorTestCase 
    {
        [Test]
        public void should_call_reset_on_wrapped_enumerator()
        {
            enumerator.Reset();
            
            A.CallTo(() => wrappedEnumerator.Reset()).MustHaveHappened();
        }
    }

    [TestFixture]
    public class Dispose
        : LockingEnumeratorTestCase 
    {
        [Test]
        public void should_call_dispose_on_wrapped_enumerator()
        {
            enumerator.Dispose();
            
            A.CallTo(() => wrappedEnumerator.Dispose()).MustHaveHappened();
        }

        [Test]
        public void should_release_lock()
        {
            enumerator.Dispose();
            
            A.CallTo(() => readerWriterLock.ExitReadLock()).MustHaveHappened();
        }
    }


}
